#region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace Osh.Media
{
	public abstract class MediaPlayerBase : IMediaPlayer, ITraceSourceProvider
	{
		private string _name;
		private ServiceRegistry _services = new ServiceRegistry();
		private Timer _playerMonitoringTimer;
		private int _playerMonitoringInterval;
		private bool _isInsideMonitoringCallback;
		private PlayerState _state = PlayerState.Closed;
		private IMedia _currentMedia;

		public MediaPlayerBase(string name)
		{
			_name = name;
			_playerMonitoringTimer = new Timer(new TimerCallback(this.OnPlayerMonitoringTimeout));
			_playerMonitoringInterval = Timeout.Infinite;
		}

		public string Name
		{
			get { return _name; }
		}

		protected int PlayerMonitoringInterval
		{
			get { return _playerMonitoringInterval; }
			set
			{
				if(_playerMonitoringInterval == value)
					return;

				_playerMonitoringInterval = value;
				if(_playerMonitoringInterval == Timeout.Infinite)
					_playerMonitoringTimer.Change(Timeout.Infinite, Timeout.Infinite);
				else
					_playerMonitoringTimer.Change(0, _playerMonitoringInterval);
			}
		}

		private void OnPlayerMonitoringTimeout(object stateInfo)
		{
			if(_isInsideMonitoringCallback)
				return;

			_isInsideMonitoringCallback = true;
			try
			{
				this.State = GetPlayerState();
			}
			catch(Exception e)
			{
				Log.Exception(this.TraceSourceInternal, e);
			}
			finally
			{
				_isInsideMonitoringCallback = false;
			}

		}

		public bool IsRunning
		{
			get { return _state != PlayerState.Closed; }
		}

		public virtual PlayerState State
		{
			get { return _state; }
			protected set
			{
				if(_state == value) return;
				
				PlayerState prevState = _state;
				_state = value;

				if(prevState == PlayerState.Closed)
					OnStart();

				OnStateChange();

				if(_state == PlayerState.Closed)
					OnExit();
			}
		}

		public virtual IMedia CurrentMedia
		{
			get { return _currentMedia; }
			protected set
			{
				_currentMedia = value;
				// Generate event even if the media has not been changed
				OnMediaChange(value);
			}
		}

		protected abstract TraceSource TraceSourceInternal { get; }
		/// <summary>
		/// This method is periodically called by MediaPlayerBase's monitoring procedure to determine the current player's state.
		/// </summary>
		/// <returns></returns>
		protected abstract PlayerState GetPlayerState();

		protected virtual void OnStateChange()
		{
			Log.Write(TraceSourceInternal, "{0} state changed to {1}.", this.Name, _state.ToString());

			if(this.StateChange != null)
				this.StateChange(this, null);
		}

		protected virtual void OnStart()
		{
			Log.Info(TraceSourceInternal, "{0} started.", this.Name);

			if(this.Start != null)
				this.Start(this, EventArgs.Empty);
		}

		protected virtual void OnExit()
		{
			Log.Info(TraceSourceInternal, "{0} exited.", this.Name);

			if(this.Exit != null)
				this.Exit(this, EventArgs.Empty);
		}

		protected virtual void OnMediaChange(IMedia media)
		{
			if(this.MediaChange != null)
				this.MediaChange(this, new MediaChangeArgs(media));
		}

		protected virtual void OnMediaStreamChange(IMediaStreamDescriptor stream)
		{
			if(this.MediaStreamChange != null)
				this.MediaStreamChange(this, new MediaStreamChangeArgs(this.CurrentMedia, stream));
		}

		#region IMediaPlayer Members
		public abstract void PlayMedia(IMedia media);
		public abstract void Play();
		public abstract void Pause();
		public abstract void Stop();

		public abstract ITimeline Timeline { get; }

		public event EventHandler Start;
		public event EventHandler Exit;
		public event EventHandler StateChange;
		public event EventHandler<MediaChangeArgs> MediaChange;
		public event EventHandler<MediaStreamChangeArgs> MediaStreamChange;
		#endregion

		#region IServiceRegistryProvider
		public virtual IServiceRegistry Services
		{
			get { return _services; }
		}
		#endregion IServiceRegistryProvider

		#region ITraceSourceProvider
		TraceSource ITraceSourceProvider.TraceSource
		{
			get { return this.TraceSourceInternal; }
		}
		#endregion ITraceSourceProvider

		public static bool IsProcessAlive(Process process)
		{
			if(process.Responding)
				return true;

			try
			{
				return !process.HasExited;
			}
			catch(Exception)
			{
				// Access to HasExited property can raise "Access denied" exception (Vista UAC), but only when process exists.
				return true;
			}
		}

	}
}
